#include <crt_argv.h>
#include <crt_str.h>
#include <crt_memory.h>


#define SPACE_LEFT(a) ((a)->len - (a)->argc - 1)


static void argv_extend(argv_t* argvp)
{
	int new_len;

	new_len = argvp->len * 2;
	argvp->argv = (char **)heap_realloc((char *) argvp->argv,(new_len + 1) * sizeof(char *));
	argvp->len = new_len;
}

static void argv_add(argv_t *argvp, ...)
{
	const char *arg;
	va_list ap;

	va_start(ap, argvp);
	while ((arg = va_arg(ap, const char *)) != 0) {
		if (SPACE_LEFT(argvp) <= 0) {
			argv_extend(argvp);
		}
		argvp->argv[argvp->argc++] = heap_strdup(arg);
	}
	va_end(ap);
	argvp->argv[argvp->argc] = 0;
}

static void argv_push_back(argv_t* argvp, const char *s)
{
	argv_add(argvp, s, 0);
}

static void argv_push_front(argv_t* argvp, const char *s)
{
	int   i;

	/* Make sure that always argvp->argc < argvp->len. */

	if (SPACE_LEFT(argvp) <= 0) {
		argv_extend(argvp);
	}
	for (i = argvp->argc; i > 0; i--) {
		argvp->argv[i] = argvp->argv[i - 1];
	}

	argvp->argv[0] = heap_strdup(s);
	argvp->argc++;
}


static char *argv_pop_back(argv_t* argvp)
{
	if (argvp->argc <= 0) {
		return NULL;
	}
	return argvp->argv[--argvp->argc];
}

static char *argv_pop_front(argv_t* argvp)
{
	char *s;
	int   i;

	if (argvp->argc <= 0) {
		return (NULL);
	}
	s = argvp->argv[0];
	argvp->argc--;
	for (i = 0; i < argvp->argc; i++) {
		argvp->argv[i] = argvp->argv[i + 1];
	}
	return s;
}


static void *argv_iter_head(ITER *iter, argv_t* argv)
{
	iter->dlen = -1;
	iter->key  = NULL;
	iter->klen = -1;

	iter->i    = 0;
	iter->size = argv->argc;
	iter->ptr  = argv->argv[0];

	iter->data = iter->ptr;
	return iter->ptr;
}

/* argv_iter_next - get the next of the array */

static void *argv_iter_next(ITER *iter, argv_t *argv)
{
	iter->i++;
	if (iter->i >= argv->argc) {
		iter->data = iter->ptr = 0;
	} else {
		iter->data = iter->ptr = argv->argv[iter->i];
	}
	return iter->ptr;
}
 
/* argv_iter_tail - get the tail of the array */

static void *argv_iter_tail(ITER *iter, argv_t *argv)
{
	iter->dlen = -1;
	iter->key  = NULL;
	iter->klen = -1;

	iter->i    = argv->argc - 1;
	iter->size = argv->argc;
	if (iter->i < 0) {
		iter->data = iter->ptr = 0;
	} else {
		iter->data = iter->ptr = argv->argv[iter->i];
	}
	return iter->ptr;
}

/* argv_iter_prev - get the prev of the array */

static void *argv_iter_prev(ITER *iter, argv_t *argv)
{
	iter->i--;
	if (iter->i < 0) {
		iter->data = iter->ptr = 0;
	} else {
		iter->data = iter->ptr = argv->argv[iter->i];
	}
	return iter->ptr;
}


/* argv_add - add string to vector */


void argv_terminate(argv_t *argvp)
{
	/* Trust that argvp->argc < argvp->len. */
	argvp->argv[argvp->argc] = 0;
}

int argv_size(argv_t *argvp)
{
	if (argvp == NULL)
		return -1;

	return argvp->argc;
}

/* argv_split - split string into token array */

argv_t* argv_split(argv_t* argvp, const char *str, const char *delim)
{
	char   *saved_string = heap_strdup(str);
	char   *bp = saved_string;
	char   *arg;

	char* save_ptr = NULL;
	while ((arg = s_strtok(bp, delim,&save_ptr)) != 0)
		argv_add(argvp, arg, (char *)0);
	argv_terminate(argvp);

	heap_free(saved_string);

	return argvp;
}


/* argv_free - destroy string array */

void argv_clean(argv_t *argvp)
{
	char  **cpp;


	for (cpp = argvp->argv; cpp < argvp->argv + argvp->argc; cpp++) {
		heap_free(*cpp);
	}
	heap_free(argvp->argv);

}

/* argv_alloc - initialize string array */

int argv_init(argv_t* argvp,int size)
{
	int     sane_len;

	argvp->len     = 0;
	sane_len       = (size < 2 ? 2 : size);
	argvp->argv    = (char **)heap_malloc((sane_len + 1) * sizeof(char *));
	argvp->len     = sane_len;
	argvp->argc    = 0;
	argvp->argv[0] = 0;

	argvp->push_back  = argv_push_back;
	argvp->push_front = argv_push_front;
	argvp->pop_back   = argv_pop_back;
	argvp->pop_front  = argv_pop_front;

	/* set the iterator callback */
	argvp->iter_head = argv_iter_head;
	argvp->iter_next = argv_iter_next;
	argvp->iter_tail = argv_iter_tail;
	argvp->iter_prev = argv_iter_prev;

	/* set the oper */
	argvp->argv_split = argv_split;
	argvp->argv_size = argv_size;
	argvp->argv_terminate = argv_terminate;
	return 1;
}



